#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <time.h>
#include <fcntl.h>
#include "Game.h"

using namespace std;

#define N_pemain 3
#define SIZE 100
#define errexit(code,str)                          \
        fprintf(stderr,"%s: %s\n",(str),strerror(code)); \
        exit(1);

//--------------------------------------------------------
typedef struct {
  long    mtype;
  char    mtext[SIZE];
} message_buf;

char controls[3][4] = {

  { 'w','a','s','d' },
  { 't','f','g','h' },

  { 'i','j','k','l' }

};

int row, col, no_pemain;

key_t key = ftok("GameMap", 1); //key untuk shared memory map
key_t key2 = ftok("Position", 1); //key untuk shared memory posisi
char sem_name[] = "shm_access";
shmid_ds *buf;
Game* g;
int loop = 1;

key_t key3 = ftok("Message", 2); //key untuk message_queue 
int msqid;

int pos[N_pemain*2];
//--------------------------------------------------------

void Access_Position(int mode)
{
  if (mode == 1)
  {
    char * shm, *s, angka[9];
    int shm_id = shmget(12345, 18, IPC_CREAT | 0666), i, j, temp;
    if (shm_id >=0)
    {
      if ((int)(shm = (char*)shmat(shm_id, NULL, 0)) == -1) {
        perror("shmat");
        exit(1);
      }
      else
      {
        s = shm;
        //print player's position
        for (j=0; j<N_pemain; ++j)
        {
          //print p1.x to shared memory
          sprintf(angka, "%c", (g->players[j]->x)+1);
          temp = strlen(angka);
          for (i=0; i<temp; ++i)
          {
            *s = angka[i];
            ++s;
          }
          
          //print p1.y to shared memory
          sprintf(angka, "%c", (g->players[j]->y)+1);
          temp = strlen(angka);
          for (i=0; i<temp; ++i)
          {
            *s = angka[i];
            ++s;
          }
        }
      }
    }
    else
    {
      perror("shmget");
      exit(1);
    }
  }
  else if (mode == 2)
  {
    char * shm, *s, angka[9];
    int shm_id = shmget(12345, 18, IPC_CREAT | 0666), i, j, temp;
    if (shm_id >=0)
    {
        if ((int)(shm = (char*)shmat(shm_id, NULL, 0)) == -1) {
          perror("shmat");
          exit(1);
        }
        else
        {
          int k, idx=0;
          int angkanya[N_pemain*2];
          
          s = shm;
          i=0;

          for (k=0; k<N_pemain*2; ++k)
          {
            angkanya[k] = (*s)-1;
            ++s;
          }
          
          for (i=0; i<N_pemain; ++i)
          {
            g->players[i]->x = angkanya[i+i];
            g->players[i]->y = angkanya[i+i+1];
            g->players[i]->image = i+'1';
          }
        }
    }
    else
    {
      perror("shmget");
      exit(1);
    }
  }
}

void check()
{    
    srand(time(NULL));
    row = rand()%20+30;
    col = rand()%20+30;
    char * shm, *s, angka[9];
    int shm_id = shmget(key, 100*100+20, IPC_CREAT | 0666), i, j, width, temp;
    if (shm_id >=0)
    {
        if ((int)(shm = (char*)shmat(shm_id, NULL, 0)) == -1) {
          perror("shmat");
          exit(1);
        }
        else if ((*shm == NULL) || (*shm <= '0') || (*shm > '9'))
        {
          s = shm;
          
          //print row to shared memory
          sprintf(angka, "%d", row);
          int temp = strlen(angka);
          
          for (i=0; i<temp; ++i)
          {
            *s = angka[i];
            ++s;
          }
          //mark
          *s = '%';
          ++s;
          
          //print col to shared memory
          sprintf(angka, "%d", col);
          temp = strlen(angka);
          
          for (i=0; i<temp; ++i)
          {
            *s = angka[i];
            ++s;
          }
          //mark
          *s = '%';
          ++s;
          
          //create Game first...
          g = Game_create(col, row, N_pemain);
          Game_init(g);
          
          //print maze to shared memory
          for (i=0; i<row; ++i)
          {
            for (j=0; j<col; ++j)
            {
              *s = g->maze->map[i*col+j];
              ++s;
            }
          }
          
          //Terminating char....
          *s = 0;
          ++s;
          Access_Position(1);
//          Game_draw(g, 0, 0, 23, 23, no_pemain-1, 8);
        }
        else
        {
          int k, idx=0;
          int angkanya[2];
          s = shm;
          i=0;
          j = strlen(shm);

          for (k=0; k<2; ++k)
          {
            for (; *s!='%'; ++i)
            {
              angka[idx++] = *s;
              ++s;
            }
            ++s;
            ++i;
            angka[idx]=0;
            angkanya[k] = atoi(angka);
            idx = 0;
          }
          
          row = angkanya[0];
          col = angkanya[1];
          g = Game_create(angkanya[1], angkanya[0], N_pemain);
          
          idx = 0;

          for (; i<j; ++i)
          {
            g->maze->map[idx++] = *s;
            ++s;
          }
          
          /*for (i=0; i<row; ++i)
          {
            for (j=0; j<col; ++j)
            {
              cout <<g->maze->map[i*col+j];
            }
            cout << "...\n";
          }*/
          for (; i<j; ++i)
          {
            g->maze->map[idx++] = *s;
            ++s;
          }
          
          Access_Position(2);
//          Game_draw(g, 0, 0, 23, 23, no_pemain-1, 8);
        }
    }
    else
      perror("shmget");
}

void del_shm()
{
  int shm_id = shmget(12345, 18, IPC_CREAT | 0666);
  char s[100];
  if (shm_id >=0)
  {
    sprintf(s,"ipcrm -m %d",shm_id);
    system(s);
  }
  shm_id = shmget(key, 100*100+20, IPC_CREAT | 0666);
  if (shm_id >=0)
  {
    sprintf(s,"ipcrm -m %d",shm_id);
    system(s);
  }
}

void get_msg_id()
{
  if ((msqid = msgget(key3, IPC_CREAT | 0666 )) < 0) {
      perror("msgget");
      exit(1);
  }
}

void *read_mq(void *ptr)
{
  message_buf  rbuf;
  char c = ' ';
  int i;

  do
  {
    if (msgrcv(msqid, &rbuf, SIZE, 1, 0) < 0)
    {
      perror("msgrcv");
      exit(1);
    }
    else
    {
      if (strlen(rbuf.mtext))
      {
        c = rbuf.mtext[0];
        for (i=0; i<N_pemain; ++i)
        {
          pos[i+i] = g->players[i]->x;
          pos[i+i+1] = g->players[i]->y;
        }
        Access_Position(2);
        for (i=0; i<N_pemain; ++i)
        {
            if (pos[i+i] != g->players[i]->x || pos[i+i+1] != g->players[i]->y)
            {
              Game_draw(g, 0, 0, 23, 23, no_pemain-1, 8);
              break;
            }
        }
        
        for (i=1; i<N_pemain; ++i)
          if ((g->players[i]->x != g->players[0]->x)||(g->players[i]->y != g->players[0]->y))
            break;
        if (i >= N_pemain)
          loop = false;
      }
    }
  }
  while (loop);
}

void *read_in(void *ptr)
{
  char c;
  message_buf sbuf;
  string s = "0";
  int i;
  
  while (loop)
  {  
    cin >> c;
    for(i = 0; i < N_pemain; ++i)
    {
	    if(c == controls[i][0]) {
		    Game_movePlayerBy(g, i, 0, -1);
		    break;
	    } else if(c == controls[i][1]) {
		    Game_movePlayerBy(g, i, -1, 0);
		    break;
	    } else if(c == controls[i][2]) {
		    Game_movePlayerBy(g, i, 0, 1);
		    break;
	    } else if(c == controls[i][3]) {
		    Game_movePlayerBy(g, i, 1, 0);
		    break;
	    }
    }
    if (i < N_pemain)
    {
      pos[i+i] = g->players[i]->x;
      pos[i+i+1] = g->players[i]->y;
      Access_Position(1);
      Game_draw(g, 0, 0, 23, 23, no_pemain-1, 8);
      
      int msgflg = IPC_CREAT | 0666;
      //kirim message
      s[0] = c;

      for (i=0; i<N_pemain; ++i)
      {
        sbuf.mtype = 1;
        (void) strcpy(sbuf.mtext, s.c_str());
        int buf_length = strlen(sbuf.mtext) + 1 ;

        if (msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT) < 0) {
          perror("msgsnd");
          exit(1);
        }
      }
    }
  }    
}
//-----



int main(int argc, char* args[]) {
  if (argc > 1)
  {
    no_pemain = atoi(args[1]);
    if (no_pemain>0 && no_pemain<=N_pemain)
    {	    
      //cek shared memory
      check();
      //inisialisasi msqid
      get_msg_id();
	    Game_draw(g, 0, 0, 23, 23, no_pemain-1, 8);
	    
      //---------------------------------------------------------------------------------------
      pthread_t thread1, thread2;
      char message1[] = "1";
      char message2[] = "2";
      int  iret1, iret2;
 
      /* Create independent threads each of which will execute function */
      iret1 = pthread_create( &thread1, NULL, read_mq, (void*) message1);
      iret2 = pthread_create( &thread2, NULL, read_in, (void*) message2);
 
      pthread_join( thread1, NULL);
      pthread_join( thread2, NULL);
      //---------------------------------------------------------------------------------------
      
      Game_destroy(g);
      del_shm();
    }
    else
    {
      printf("No pemain hanya antara 1..%d\n",N_pemain);
    }
  }
  else
  {
    printf("sintaks : ./main <no_pemain>\n");
  }

  return 0;

}